package com.corewillsoft.fireflies.gameobjects;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.anddev.andengine.entity.primitive.Rectangle;

import com.corewillsoft.fireflies.gamelaws.BaseLaw;
import com.corewillsoft.fireflies.gamelaws.LawFactory;
import com.corewillsoft.fireflies.gamelaws.LawType;
import com.corewillsoft.fireflies.ui.GameActivity.GameLayerType;

public abstract class GameObject extends Rectangle {

	// minimal and maximum values of firefly coordinates (borders of game field)
	private static AreaObject gameRestrictedArea;

	private final AreaObject area;
	private Set<BaseLaw> createdLawsSet;
	private List<LawType> createdLawTypes;
	private GameLayerType layerType;

	public GameObject(int x, int y) {
		super(x, y, 0, 0);

		area = new AreaObject(x, x, y, y);
		setPosition(x, y);
	}

	/**
	 * set supported area. Set here area of gameActivity
	 * @param areaObject
	 */
	public static void setGameRestrictedArea(AreaObject areaObject) {
		gameRestrictedArea = areaObject;
	}

	/**
	 * set top left coordinate of object
	 */
	@Override
	public void setPosition(float pX, float pY) {
		float realX = validateXCoordinate(pX);
		float realY = validateYCoordinate(pY);

		area.updatePosition(realX, realY);
		super.setPosition(realX, realY);
	}

	/**
	 * set width of GameObject
	 */
	@Override
	public void setWidth(float pWidth) {
		super.setWidth(pWidth);
		area.setWidth(pWidth);
		super.setPosition(validateYCoordinate(area.getMinX()), area.getMinY());
	}

	/**
	 * set height of GameObject
	 */
	@Override
	public void setHeight(float pHeight) {
		super.setHeight(pHeight);
		area.setHeight(pHeight);
		super.setPosition(area.getMinX(), validateYCoordinate(area.getMinY()));
	}

	/**
	 * @return return {@link AreaObject} where object is placed.
	 */
	public AreaObject getArea() {
		return area;
	}

	/**
	 * validate X. check objects present in real world coordinates
	 * @param pX
	 * @return validated pX
	 */
	private float validateXCoordinate(float pX) {

		float realX = pX;
		if (gameRestrictedArea.getMinX() >= realX)
			realX = gameRestrictedArea.getMinX();
		else if (gameRestrictedArea.getMaxX() < (realX + area.getWidth()))
			realX = gameRestrictedArea.getMaxX() - area.getWidth();

		return realX;
	}

	/**
	 * validate Y. check users present in real world coordinates
	 * @param pY
	 * @return validated pY
	 */
	private float validateYCoordinate(float pY) {

		float realY = pY;
		if (realY <= gameRestrictedArea.getMinY()) {
			realY = gameRestrictedArea.getMinY();
		} else if ((realY + area.getHeight()) > (gameRestrictedArea.getMaxX())) {
			realY = gameRestrictedArea.getMaxX() - area.getHeight();
		}

		return realY;
	}

	/**
	 * get layer type of the game world scene
	 */
	public GameLayerType getLayerType() {
		return layerType;
	}

	/**
	 * set layer type of the game world scene
	 */
	public void setLayerType(GameLayerType obstacles) {
		this.layerType = obstacles;
	}

	/**
	 * check if law supported. If law supported, laws with this type will have influence.
	 * @param lawType
	 *            type of law
	 * @return true if law is supported
	 */
	public boolean isLawSupported(LawType lawType) {
		return getSupportedLaws().contains(lawType);
	}

	/**
	 * @return true if there is 1 or more supported laws
	 */
	public boolean hasSupportedLaws() {
		return (getSupportedLaws().size() > 0);
	}

	public Set<BaseLaw> getCreateLaws() {
		if (isThereNewLaws()) {
			Set<BaseLaw> lawsSet = new HashSet<BaseLaw>();
			for (LawType lawType : getCreatedLaws()) {
				BaseLaw law = LawFactory.getLaw(lawType);
				law.setParent(this);
				law.initializeInfluenceArea();
				lawsSet.add(law);
			}

			createdLawTypes = getCreatedLaws();
			createdLawsSet = lawsSet;
		}
		return createdLawsSet;
	}

	public boolean isThereNewLaws() {
		boolean isLawTypesEquals = ((createdLawTypes != null) && createdLawTypes.equals(getCreatedLaws()));
		return (createdLawTypes == null) || !isLawTypesEquals;
	}

	public boolean isOwnLaw(BaseLaw law) {
		return createdLawsSet.contains(law);
	}

	public abstract void updateState();

	/**
	 * Abstract method, which help to change supported laws flexibly
	 * @return List with created laws types
	 */

	protected abstract List<LawType> getSupportedLaws();

	/**
	 * Abstract method, which help to change create laws flexibly
	 * @return List with created laws types
	 */
	protected abstract List<LawType> getCreatedLaws();

	public abstract boolean isActive();
}
